#include "StatusCode.h"

#include "FlotMessage.h"

#include <boost/foreach.hpp>

using namespace std;
using namespace boost;

/** Constructeur par défaut.
 * Permet de construire un StatusCode avec ses différents champs.
 *
 * @param[in] etat \e true ou \e false, suivant la réussite ou l'échec
 * d'une méthode (par défaut \e true).
 * @param[in] val valeur de renvoi de la fonction (par défaut, 0).
 * @param[in] code valeur du code d'erreur (par défaut, 0).
 * @param[in] message valeur du message que l'on veut propager (par
 * défaut "").
 */
StatusCode::StatusCode(const bool& etat, const int& val, const int& code,
	     const string& message)
  : Etat( etat ), Code( code ), Message( message )
{
  Valeurs.push_back( val );
}

/** Constructeur par défaut.
 * Permet de construire un StatusCode avec ses différents champs.
 *
 * @param[in] etat \e true ou \e false, suivant la réussite ou l'échec
 * d'une méthode.
 * @param[in] val valeurs de renvoi de la fonction.
 * @param[in] code valeur du code d'erreur (par défaut, 0).
 * @param[in] message valeur du message que l'on veut propager (par
 * défaut "").
 */
StatusCode::StatusCode(const bool& etat,  const vector< int >& val,
		       const int& code, const string& message)
  : Etat( etat ), Code( code ), Message( message )
{
  Valeurs.assign( val.begin(), val.end() );
}

/** Constructeur de copie
 * Permet de réaliser une copie proprement d'un StatusCode.
 *
 * @param[in] sc StatusCode à copier.
 */
StatusCode::StatusCode(const StatusCode& sc)
  : Etat( sc.Etat ), Code( sc.Code ), Message( sc.Message )
{
  Valeurs.assign( sc.Valeurs.begin(), sc.Valeurs.end() );
}

/**
 * Destructeur, ne fait rien de spécial.
 */
StatusCode::~StatusCode()
{}

/**
 * Permet de convertir un StatusCode en bool, afin de pouvoir écrire
 * simplement :
 * @code
 * StatusCode sc( maFonction );
 *
 * if ( sc )
 * { ... }
 * @endcode
 *
 * L'opérateur renvoie simplement la valeur du champ Etat.
 */
StatusCode::operator bool() const
{
  return Etat;
}

/**
 * Permet de convertir un StatusCode en int. L'opérateur renvoie alors
 * la valeur du champ Valeur.
 */
StatusCode::operator int() const
{
  return Valeurs.front();
}

/**
 * Permet d'accéder au champ Etat.
 *
 * @return Etat.
 */
const bool& StatusCode::etat() const
{
  return Etat;
}

/**
 * Permet d'accéder au champ Valeur. Lors de l'utilisation par défaut,
 * il n'y a qu'une seule valeur. Sinon il faut spécifier l'indice par
 * argument.
 *
 * @param[in] num indice de la valeur voulue.
 *
 * @return une référence constante sur la valeur du champ voulu, si
 * l'indice est trop grand, alors on renvoie la \e première valeur.
 */
const int& StatusCode::valeur(const unsigned& num) const
{
  if ( num >= Valeurs.size() )
    return Valeurs.front();
  else
    return Valeurs[ num ];
}

/**
 * Permet d'accéder au champ Code.
 *
 * @return Code.
 */
const int& StatusCode::code() const
{
  return Code;
}

/**
 * Permet d'accéder au champ Message.
 *
 * @return Message.
 */
const string& StatusCode::message() const
{
  return Message;
}

/**
 * Permet de récupérer le nombre de valeurs renvoyées.
 *
 * @return la taille du conteneur Valeurs.
 */
unsigned StatusCode::nombre() const
{
  return Valeurs.size();
}

/**
 * Permet de changer l'état d'un StatusCode.
 *
 * @param[in] etat nouvelle valeur pour Etat.
 */
void StatusCode::setEtat( const bool& etat )
{
  Etat = etat;
}

/**
 * Permet de changer une valeur existante d'un StatusCode. Par défaut
 * on change la première valeur, sinon la valeur demandée si
 * existante.
 *
 * @param[in] valeur nouvelle valeur pour Valeurs[ i ].
 * @param[in] num indice de la valeur à changer.
 */
void StatusCode::setValeur( const int& valeur, const unsigned& num )
{
  if ( num == -1u )
    Valeurs[ 0 ] = valeur;
  else if ( num < Valeurs.size() )
    Valeurs[ num ] = valeur;
}

/**
 * Permet de changer toutes les valeurs d'un StatusCode.
 *
 * @param[in] valeurs nouvelles valeurs de Valeurs.
 */
void StatusCode::setValeurs( const std::vector< int >& valeurs )
{
  Valeurs.assign( valeurs.begin(), valeurs.end() );
}

/**
 * Permet d'ajouter une valeur à un StatusCode. En principe on ne doit
 * pas en avoir besoin.
 *
 * @param[in] valeur nouvelle valeur à ajouter à Valeurs.
 *
 * @return la nouvelle taille du conteneur Valeurs.
 */
unsigned StatusCode::ajoutValeur( const int& valeur )
{
  Valeurs.push_back( valeur );

  return nombre();
}

/**
 * Permet de changer le code d'erreur.
 *
 * @param[in] code nouvelle valeur de Code.
 */
void StatusCode::setCode( const int& code )
{
  Code = code;
}

/**
 * Permet de changer le message.
 *
 * @param[in] message nouvelle valeur de Message.
 */
void StatusCode::setMessage( const string& message )
{
  Message = message;
}

ostream& operator<<(ostream& os, const StatusCode& sc)
{
  if ( sc.nombre() == 1 )
    os << "Valeur : " << sc.valeur() << ", Code : " << sc.code() << ", "
       << sc.message();
  else
    {
      os << "Valeurs : ";
      BOOST_FOREACH( int i, sc.Valeurs )
	os << i << ", ";
      os << ", Code : " << sc.code() << ", "
	 << sc.message();
    }
  return os;
}

FlotMessage& operator<<(FlotMessage& flot, const StatusCode& sc)
{
  if ( sc.nombre() == 1 )
    flot << "Valeur : " << sc.valeur() << ", Code : " << sc.code() << ", "
       << sc.message();
  else
    {
      flot << "Valeurs : ";
      BOOST_FOREACH( int i, sc.Valeurs )
	flot << i << ", ";
      flot << ", Code : " << sc.code() << ", "
	 << sc.message();
    }
  return flot;
}

/**
 * Permet de réaliser l'affichage d'une erreur. Suivant la valeur
 * (strictement positive ou négative) du code, on utilise warning() ou
 * error(). Si \c Status est \e true, alors on ne fait rien.
 *
 * Cette méthpde est rpévue pour être utilisée comme suit :
 * @code
 * StatusCode sc( ... );
 *
 * if ( ! sc )
 *   sc.afficher();
 *
 * @endcode
 */
void StatusCode::afficher() const
{
  FlotMessage& mess = flotMessageGlobal();
  if ( code() < 0 )
    mess << ERR << "Erreur numéro " << code() << ", " << message() << endmsg
	 << "Le programme va s'arrêter" << endmsg;
  else if( code() > 0 )
    mess << WARN << "Erreur numéro " << code() << ", " << message() << endmsg;
  else
    mess << INFO << *this << endmsg;
}
